﻿// -----------------------------------------------------------------------
// <copyright file="MemoryBuffer.cs" company="none">
// Copyright (c) 2012 M. Alberti, xanatos(at)live.it
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
// </copyright>
// -----------------------------------------------------------------------

namespace FluentSerializer.Utilities
{
    using System;

    /// <summary>
    /// A mix of MemoryStream and BinaryReader/BinaryWriter. Note that it isn't a stream,
    /// so you can write wherever you want. If you want to use stream interfaces on 
    /// MemoryBuffer, use <seealso cref="BinaryReaderStream">BinaryReaderStream</seealso> and 
    /// <seealso cref="BinaryWriterStream">BinaryWriterStream</seealso>
    /// </summary>
    public class MemoryBuffer : IDisposable
    {
        /// <summary>
        /// 
        /// </summary>
        /// <param name="eraseOnDispose"></param>
        public MemoryBuffer(bool eraseOnDispose = false)
            : this(0, eraseOnDispose)
        {
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="capacity"></param>
        /// <param name="eraseOnDispose"></param>
        public MemoryBuffer(int capacity, bool eraseOnDispose = false)
        {
            this.Buffer = new byte[capacity];
            this.CanResize = true;
            this.EraseOnDispose = eraseOnDispose;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="buffer"></param>
        /// <param name="eraseOnDispose"></param>
        public MemoryBuffer(byte[] buffer, bool eraseOnDispose = false)
            : this(buffer, 0, buffer != null ? buffer.Length : 0, eraseOnDispose)
        {
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="buffer"></param>
        /// <param name="index"></param>
        /// <param name="count"></param>
        /// <param name="eraseOnDispose"></param>
        public MemoryBuffer(byte[] buffer, int index, int count, bool eraseOnDispose = false)
        {
            if (buffer == null)
            {
                throw new ArgumentNullException("buffer");
            }

            if (index < 0 || index > buffer.Length)
            {
                throw new IndexOutOfRangeException("index");
            }

            if (count < 0 || index + count > buffer.Length)
            {
                throw new IndexOutOfRangeException("count");
            }

            this.Buffer = buffer;
            this.Length = count;
            this.Offset = index;
            this.CanResize = false;
            this.EraseOnDispose = eraseOnDispose;
        }

        /// <summary>
        /// 
        /// </summary>
        ~MemoryBuffer()
        {
            this.Dispose(false);
        }

        /// <summary>
        /// 
        /// </summary>
        public byte[] Buffer { get; protected set; }

        /// <summary>
        /// 
        /// </summary>
        public int Length { get; set; }

        /// <summary>
        /// 
        /// </summary>
        public int Offset { get; protected set; }

        /// <summary>
        /// 
        /// </summary>
        protected bool CanResize { get; set; }

        /// <summary>
        /// 
        /// </summary>
        protected bool EraseOnDispose { get; set; }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="position"></param>
        /// <param name="eraseOnDispose"></param>
        /// <returns></returns>
        public MemoryBuffer Clone(int position = 0, bool eraseOnDispose = false)
        {
            int offset = this.Offset + position;
            int length = this.Length - position;

            var buffer = new MemoryBuffer(length, eraseOnDispose);
            System.Buffer.BlockCopy(this.Buffer, offset, buffer.Buffer, 0, length);
            buffer.Length = length;

            return buffer;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="offset"></param>
        /// <returns></returns>
        public sbyte ReadSByte(int offset)
        {
            return unchecked((sbyte)this.ReadByte(offset));
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="offset"></param>
        /// <returns></returns>
        public byte ReadByte(int offset)
        {
            if (offset < 0 || offset + sizeof(byte) > this.Length)
            {
                throw new IndexOutOfRangeException("offset");
            }

            offset += this.Offset;

            return this.Buffer[offset];
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="offset"></param>
        /// <returns></returns>
        public bool ReadBoolean(int offset)
        {
            if (offset < 0 || offset + sizeof(bool) > this.Length)
            {
                throw new IndexOutOfRangeException("offset");
            }

            return new PrimitiveTypeConverter { ByteA = this.ReadByte(offset) }.BoolA;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="offset"></param>
        /// <returns></returns>
        public short ReadInt16(int offset)
        {
            return unchecked((short)this.ReadUInt16(offset));
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="offset"></param>
        /// <returns></returns>
        public ushort ReadUInt16(int offset)
        {
            if (offset < 0 || offset + sizeof(ushort) > this.Length)
            {
                throw new IndexOutOfRangeException("offset");
            }

            offset += this.Offset;

            unchecked
            {
                ushort ret = this.Buffer[offset];
                ret |= (ushort)(((ushort)this.Buffer[offset + 1]) << 8);

                return ret;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="offset"></param>
        /// <returns></returns>
        public char ReadChar(int offset)
        {
            return new PrimitiveTypeConverter { UInt16A = this.ReadUInt16(offset) }.CharA;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="offset"></param>
        /// <returns></returns>
        public int ReadInt32(int offset)
        {
            return unchecked((int)this.ReadUInt32(offset));
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="offset"></param>
        /// <returns></returns>
        public uint ReadUInt32(int offset)
        {
            if (offset < 0 || offset + sizeof(uint) > this.Length)
            {
                throw new IndexOutOfRangeException("offset");
            }

            offset += this.Offset;

            unchecked
            {
                uint ret = this.Buffer[offset];
                ret |= (uint)(((uint)this.Buffer[offset + 1]) << 8);
                ret |= (uint)(((uint)this.Buffer[offset + 2]) << 16);
                ret |= (uint)(((uint)this.Buffer[offset + 3]) << 24);

                return ret;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="offset"></param>
        /// <returns></returns>
        public long ReadInt64(int offset)
        {
            return unchecked((long)this.ReadUInt64(offset));
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="offset"></param>
        /// <returns></returns>
        public ulong ReadUInt64(int offset)
        {
            if (offset < 0 || offset + sizeof(ulong) > this.Length)
            {
                throw new IndexOutOfRangeException("offset");
            }

            offset += this.Offset;

            unchecked
            {
                ulong ret = this.Buffer[offset];
                ret |= (ulong)(((ulong)this.Buffer[offset + 1]) << 8);
                ret |= (ulong)(((ulong)this.Buffer[offset + 2]) << 16);
                ret |= (ulong)(((ulong)this.Buffer[offset + 3]) << 24);
                ret |= (ulong)(((ulong)this.Buffer[offset + 4]) << 32);
                ret |= (ulong)(((ulong)this.Buffer[offset + 5]) << 40);
                ret |= (ulong)(((ulong)this.Buffer[offset + 6]) << 48);
                ret |= (ulong)(((ulong)this.Buffer[offset + 7]) << 56);

                return ret;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="offset"></param>
        /// <returns></returns>
        public float ReadSingle(int offset)
        {
            if (offset < 0 || offset + sizeof(float) > this.Length)
            {
                throw new IndexOutOfRangeException("offset");
            }

            return new PrimitiveTypeConverter { UInt32A = this.ReadUInt32(offset) }.Single;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="offset"></param>
        /// <returns></returns>
        public double ReadDouble(int offset)
        {
            if (offset < 0 || offset + sizeof(double) > this.Length)
            {
                throw new IndexOutOfRangeException("offset");
            }

            return new PrimitiveTypeConverter { UInt64 = this.ReadUInt64(offset) }.Double;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="offset"></param>
        /// <returns></returns>
        public IntPtr ReadIntPtr(int offset)
        {
            if (IntPtr.Size == sizeof(int))
            {
                return new PrimitiveTypeConverter { UInt32A = this.ReadUInt32(offset) }.IntPtr;
            }

            return new PrimitiveTypeConverter { UInt64 = this.ReadUInt64(offset) }.IntPtr;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="offset"></param>
        /// <returns></returns>
        public UIntPtr ReadUIntPtr(int offset)
        {
            if (UIntPtr.Size == sizeof(uint))
            {
                return new PrimitiveTypeConverter { UInt32A = this.ReadUInt32(offset) }.UIntPtr;
            }

            return new PrimitiveTypeConverter { UInt64 = this.ReadUInt64(offset) }.UIntPtr;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="offset"></param>
        /// <param name="buffer"></param>
        /// <param name="bufferOffset"></param>
        /// <param name="count"></param>
        public void Read(int offset, sbyte[] buffer, int bufferOffset, int count)
        {
            this.Read(offset, buffer, sizeof(sbyte), bufferOffset, count);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="offset"></param>
        /// <param name="buffer"></param>
        /// <param name="bufferOffset"></param>
        /// <param name="count"></param>
        public void Read(int offset, byte[] buffer, int bufferOffset, int count)
        {
            this.Read(offset, buffer, sizeof(byte), bufferOffset, count);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="offset"></param>
        /// <param name="buffer"></param>
        /// <param name="bufferOffset"></param>
        /// <param name="count"></param>
        public void Read(int offset, bool[] buffer, int bufferOffset, int count)
        {
            this.Read(offset, buffer, sizeof(bool), bufferOffset, count);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="offset"></param>
        /// <param name="buffer"></param>
        /// <param name="bufferOffset"></param>
        /// <param name="count"></param>
        public void Read(int offset, short[] buffer, int bufferOffset, int count)
        {
            this.Read(offset, buffer, sizeof(short), bufferOffset, count);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="offset"></param>
        /// <param name="buffer"></param>
        /// <param name="bufferOffset"></param>
        /// <param name="count"></param>
        public void Read(int offset, ushort[] buffer, int bufferOffset, int count)
        {
            this.Read(offset, buffer, sizeof(ushort), bufferOffset, count);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="offset"></param>
        /// <param name="buffer"></param>
        /// <param name="bufferOffset"></param>
        /// <param name="count"></param>
        public void Read(int offset, char[] buffer, int bufferOffset, int count)
        {
            this.Read(offset, buffer, sizeof(char), bufferOffset, count);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="offset"></param>
        /// <param name="buffer"></param>
        /// <param name="bufferOffset"></param>
        /// <param name="count"></param>
        public void Read(int offset, int[] buffer, int bufferOffset, int count)
        {
            this.Read(offset, buffer, sizeof(int), bufferOffset, count);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="offset"></param>
        /// <param name="buffer"></param>
        /// <param name="bufferOffset"></param>
        /// <param name="count"></param>
        public void Read(int offset, uint[] buffer, int bufferOffset, int count)
        {
            this.Read(offset, buffer, sizeof(uint), bufferOffset, count);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="offset"></param>
        /// <param name="buffer"></param>
        /// <param name="bufferOffset"></param>
        /// <param name="count"></param>
        public void Read(int offset, long[] buffer, int bufferOffset, int count)
        {
            this.Read(offset, buffer, sizeof(long), bufferOffset, count);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="offset"></param>
        /// <param name="buffer"></param>
        /// <param name="bufferOffset"></param>
        /// <param name="count"></param>
        public void Read(int offset, ulong[] buffer, int bufferOffset, int count)
        {
            this.Read(offset, buffer, sizeof(ulong), bufferOffset, count);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="offset"></param>
        /// <param name="buffer"></param>
        /// <param name="bufferOffset"></param>
        /// <param name="count"></param>
        public void Read(int offset, float[] buffer, int bufferOffset, int count)
        {
            this.Read(offset, buffer, sizeof(float), bufferOffset, count);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="offset"></param>
        /// <param name="buffer"></param>
        /// <param name="bufferOffset"></param>
        /// <param name="count"></param>
        public void Read(int offset, double[] buffer, int bufferOffset, int count)
        {
            this.Read(offset, buffer, sizeof(double), bufferOffset, count);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="offset"></param>
        /// <param name="buffer"></param>
        /// <param name="bufferOffset"></param>
        /// <param name="count"></param>
        public void Read(int offset, IntPtr[] buffer, int bufferOffset, int count)
        {
            this.Read(offset, buffer, IntPtr.Size, bufferOffset, count);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="offset"></param>
        /// <param name="buffer"></param>
        /// <param name="bufferOffset"></param>
        /// <param name="count"></param>
        public void Read(int offset, UIntPtr[] buffer, int bufferOffset, int count)
        {
            this.Read(offset, buffer, UIntPtr.Size, bufferOffset, count);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="offset"></param>
        /// <param name="buffer"></param>
        /// <param name="sizeElement"></param>
        /// <param name="bufferOffset"></param>
        /// <param name="count"></param>
        public void Read(int offset, Array buffer, int sizeElement, int bufferOffset, int count)
        {
            count *= sizeElement;

            if (offset < 0 || offset + count > this.Length)
            {
                throw new IndexOutOfRangeException("offset");
            }

            offset += this.Offset;

            if (count == 0)
            {
                return;
            }

            bufferOffset *= sizeElement;

            System.Buffer.BlockCopy(this.Buffer, offset, buffer, bufferOffset, count);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="offset"></param>
        /// <param name="value"></param>
        public void Write(int offset, sbyte value)
        {
            this.Write(offset, unchecked((byte)value));
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="offset"></param>
        /// <param name="value"></param>
        public void Write(int offset, byte value)
        {
            if (offset < 0 || offset > this.Length)
            {
                throw new IndexOutOfRangeException("offset");
            }

            offset += this.Offset;

            if (offset + sizeof(byte) > this.Buffer.Length)
            {
                this.EnsureCapacity(offset + sizeof(byte));
            }

            this.Buffer[offset] = value;
            this.Length += sizeof(byte);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="offset"></param>
        /// <param name="value"></param>
        public void Write(int offset, bool value)
        {
            this.Write(offset, new PrimitiveTypeConverter { BoolA = value }.ByteA);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="offset"></param>
        /// <param name="value"></param>
        public void Write(int offset, short value)
        {
            this.Write(offset, unchecked((ushort)value));
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="offset"></param>
        /// <param name="value"></param>
        public void Write(int offset, ushort value)
        {
            if (offset < 0 || offset > this.Length)
            {
                throw new IndexOutOfRangeException("offset");
            }

            offset += this.Offset;

            if (offset + sizeof(ushort) > this.Buffer.Length)
            {
                this.EnsureCapacity(offset + sizeof(ushort));
            }

            unchecked
            {
                this.Buffer[offset] = (byte)value;
                this.Buffer[offset + 1] = (byte)(value >> 8);
            }

            this.Length += sizeof(ushort);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="offset"></param>
        /// <param name="value"></param>
        public void Write(int offset, char value)
        {
            this.Write(offset, (char)value);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="offset"></param>
        /// <param name="value"></param>
        public void Write(int offset, int value)
        {
            this.Write(offset, unchecked((uint)value));
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="offset"></param>
        /// <param name="value"></param>
        public void Write(int offset, uint value)
        {
            if (offset < 0 || offset > this.Length)
            {
                throw new IndexOutOfRangeException("offset");
            }

            offset += this.Offset;

            if (offset + sizeof(uint) > this.Buffer.Length)
            {
                this.EnsureCapacity(offset + sizeof(uint));
            }

            unchecked
            {
                this.Buffer[offset] = (byte)value;
                this.Buffer[offset + 1] = (byte)(value >> 8);
                this.Buffer[offset + 2] = (byte)(value >> 16);
                this.Buffer[offset + 3] = (byte)(value >> 24);
            }

            this.Length += sizeof(uint);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="offset"></param>
        /// <param name="value"></param>
        public void Write(int offset, long value)
        {
            this.Write(offset, unchecked((ulong)value));
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="offset"></param>
        /// <param name="value"></param>
        public void Write(int offset, ulong value)
        {
            if (offset < 0 || offset > this.Length)
            {
                throw new IndexOutOfRangeException("offset");
            }

            offset += this.Offset;

            if (offset + sizeof(ulong) > this.Buffer.Length)
            {
                this.EnsureCapacity(offset + sizeof(ulong));
            }

            unchecked
            {
                this.Buffer[offset] = (byte)value;
                this.Buffer[offset + 1] = (byte)(value >> 8);
                this.Buffer[offset + 2] = (byte)(value >> 16);
                this.Buffer[offset + 3] = (byte)(value >> 24);
                this.Buffer[offset + 4] = (byte)(value >> 32);
                this.Buffer[offset + 5] = (byte)(value >> 40);
                this.Buffer[offset + 6] = (byte)(value >> 48);
                this.Buffer[offset + 7] = (byte)(value >> 56);
            }

            this.Length += sizeof(ulong);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="offset"></param>
        /// <param name="value"></param>
        public void Write(int offset, float value)
        {
            this.Write(offset, new PrimitiveTypeConverter { Single = value }.UInt32A);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="offset"></param>
        /// <param name="value"></param>
        public void Write(int offset, double value)
        {
            this.Write(offset, new PrimitiveTypeConverter { Double = value }.UInt64);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="offset"></param>
        /// <param name="value"></param>
        public void Write(int offset, IntPtr value)
        {
            if (IntPtr.Size == sizeof(int))
            {
                this.Write(offset, new PrimitiveTypeConverter { IntPtr = value }.UInt32A);
            }
            else
            {
                this.Write(offset, new PrimitiveTypeConverter { IntPtr = value }.UInt64);
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="offset"></param>
        /// <param name="value"></param>
        public void Write(int offset, UIntPtr value)
        {
            if (UIntPtr.Size == sizeof(uint))
            {
                this.Write(offset, new PrimitiveTypeConverter { UIntPtr = value }.UInt32A);
            }
            else
            {
                this.Write(offset, new PrimitiveTypeConverter { UIntPtr = value }.UInt64);
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="offset"></param>
        /// <param name="buffer"></param>
        /// <param name="bufferOffset"></param>
        /// <param name="count"></param>
        public void Write(int offset, sbyte[] buffer, int bufferOffset, int count)
        {
            this.Write(offset, buffer, sizeof(sbyte), bufferOffset, count);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="offset"></param>
        /// <param name="buffer"></param>
        /// <param name="bufferOffset"></param>
        /// <param name="count"></param>
        public void Write(int offset, byte[] buffer, int bufferOffset, int count)
        {
            this.Write(offset, buffer, sizeof(byte), bufferOffset, count);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="offset"></param>
        /// <param name="buffer"></param>
        /// <param name="bufferOffset"></param>
        /// <param name="count"></param>
        public void Write(int offset, bool[] buffer, int bufferOffset, int count)
        {
            this.Write(offset, buffer, sizeof(bool), bufferOffset, count);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="offset"></param>
        /// <param name="buffer"></param>
        /// <param name="bufferOffset"></param>
        /// <param name="count"></param>
        public void Write(int offset, short[] buffer, int bufferOffset, int count)
        {
            this.Write(offset, buffer, sizeof(short), bufferOffset, count);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="offset"></param>
        /// <param name="buffer"></param>
        /// <param name="bufferOffset"></param>
        /// <param name="count"></param>
        public void Write(int offset, ushort[] buffer, int bufferOffset, int count)
        {
            this.Write(offset, buffer, sizeof(ushort), bufferOffset, count);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="offset"></param>
        /// <param name="buffer"></param>
        /// <param name="bufferOffset"></param>
        /// <param name="count"></param>
        public void Write(int offset, char[] buffer, int bufferOffset, int count)
        {
            this.Write(offset, buffer, sizeof(char), bufferOffset, count);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="offset"></param>
        /// <param name="buffer"></param>
        /// <param name="bufferOffset"></param>
        /// <param name="count"></param>
        public void Write(int offset, int[] buffer, int bufferOffset, int count)
        {
            this.Write(offset, buffer, sizeof(int), bufferOffset, count);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="offset"></param>
        /// <param name="buffer"></param>
        /// <param name="bufferOffset"></param>
        /// <param name="count"></param>
        public void Write(int offset, uint[] buffer, int bufferOffset, int count)
        {
            this.Write(offset, buffer, sizeof(uint), bufferOffset, count);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="offset"></param>
        /// <param name="buffer"></param>
        /// <param name="bufferOffset"></param>
        /// <param name="count"></param>
        public void Write(int offset, long[] buffer, int bufferOffset, int count)
        {
            this.Write(offset, buffer, sizeof(long), bufferOffset, count);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="offset"></param>
        /// <param name="buffer"></param>
        /// <param name="bufferOffset"></param>
        /// <param name="count"></param>
        public void Write(int offset, ulong[] buffer, int bufferOffset, int count)
        {
            this.Write(offset, buffer, sizeof(ulong), bufferOffset, count);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="offset"></param>
        /// <param name="buffer"></param>
        /// <param name="bufferOffset"></param>
        /// <param name="count"></param>
        public void Write(int offset, float[] buffer, int bufferOffset, int count)
        {
            this.Write(offset, buffer, sizeof(float), bufferOffset, count);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="offset"></param>
        /// <param name="buffer"></param>
        /// <param name="bufferOffset"></param>
        /// <param name="count"></param>
        public void Write(int offset, double[] buffer, int bufferOffset, int count)
        {
            this.Write(offset, buffer, sizeof(double), bufferOffset, count);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="offset"></param>
        /// <param name="buffer"></param>
        /// <param name="bufferOffset"></param>
        /// <param name="count"></param>
        public void Write(int offset, IntPtr[] buffer, int bufferOffset, int count)
        {
            this.Write(offset, buffer, IntPtr.Size, bufferOffset, count);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="offset"></param>
        /// <param name="buffer"></param>
        /// <param name="bufferOffset"></param>
        /// <param name="count"></param>
        public void Write(int offset, UIntPtr[] buffer, int bufferOffset, int count)
        {
            this.Write(offset, buffer, UIntPtr.Size, bufferOffset, count);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="offset"></param>
        /// <param name="buffer"></param>
        /// <param name="sizeElement"></param>
        /// <param name="bufferOffset"></param>
        /// <param name="count"></param>
        public void Write(int offset, Array buffer, int sizeElement, int bufferOffset, int count)
        {
            if (count == 0)
            {
                return;
            }

            bufferOffset *= sizeElement;
            count *= sizeElement;

            offset += this.Offset;

            this.EnsureCapacity(offset + count);

            System.Buffer.BlockCopy(buffer, bufferOffset, this.Buffer, offset, count);
            this.Length += count;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="capacity"></param>
        public void EnsureCapacity(int capacity)
        {
            capacity -= this.Offset;

            if (this.Buffer.Length >= capacity)
            {
                return;
            }

            if (!this.CanResize)
            {
                throw new NotSupportedException();
            }

            int capacity2 = this.Buffer.Length != 0 ? this.Buffer.Length * 2 : 256;

            if (capacity2 < capacity)
            {
                capacity2 = capacity;
            }

            byte[] tmp = this.Buffer;
            Array.Resize(ref tmp, capacity2);
            this.Erase();
            this.Buffer = tmp;
        }

        /// <summary>
        /// 
        /// </summary>
        public void Clear()
        {
            if (this.Length == 0)
            {
                return;
            }

            this.Erase();
            this.Length = 0;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="count"></param>
        /// <param name="resize"></param>
        public void TrimStart(int count, bool resize = false)
        {
            if (count < 0 || count > this.Length)
            {
                throw new ArgumentOutOfRangeException("count");
            }

            if (count != 0 || this.Offset > 0)
            {
                int offset = this.Offset + count;

                if (resize)
                {
                    var tmp = new byte[this.Length - count];
                    System.Buffer.BlockCopy(this.Buffer, offset, tmp, 0, tmp.Length);
                    this.Buffer = tmp;
                }
                else
                {
                    System.Buffer.BlockCopy(this.Buffer, offset, this.Buffer, 0, this.Length - count);
                }

                this.Length -= count;
            }

            this.Offset = 0;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="count"></param>
        /// <param name="resize"></param>
        public void TrimEnd(int count, bool resize = false)
        {
            if (count < 0)
            {
                throw new ArgumentOutOfRangeException("count");
            }

            int length = this.Length - count;

            if (length < 0)
            {
                throw new ArgumentOutOfRangeException("count");
            }

            if (length == this.Buffer.Length)
            {
                return;
            }

            if (resize && !this.CanResize)
            {
                throw new NotSupportedException();
            }

            if (resize)
            {
                byte[] tmp = this.Buffer;
                Array.Resize(ref tmp, this.Offset + length);
                this.Buffer = tmp;
                this.Length = length;
            }
            else
            {
                this.Length = length;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public void Dispose()
        {
            this.Dispose(true);
            GC.SuppressFinalize(this);
        }

        /// <summary>
        /// 
        /// </summary>
        protected void Erase()
        {
            if (this.EraseOnDispose)
            {
                Array.Clear(this.Buffer, 0, this.Offset + this.Length);
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="disposing"></param>
        protected virtual void Dispose(bool disposing)
        {
            if (this.Buffer != null)
            {
                this.Erase();
                this.Buffer = null;
            }
        }
    }
}